package com.salinity.kun.task;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.math3.fitting.PolynomialCurveFitter;
import org.apache.commons.math3.fitting.WeightedObservedPoints;
import org.apache.commons.math3.geometry.euclidean.threed.Line;
import org.apache.commons.math3.stat.regression.SimpleRegression;

import com.salinity.kun.algorithm.harmonic.TideSymbol;
import com.salinity.kun.helper.BaseHydrologyDataHelper;
import com.salinity.kun.helper.ConstantHelper;
import com.salinity.kun.helper.HarmonicHelper;
import com.salinity.kun.helper.MatrixHelper;
import com.salinity.kun.model.StationDateData;
import com.salinity.kun.util.DateUtil;
import com.salinity.kun.util.PathUtil;

import Jama.Matrix;

public class HarmonicAnalysisTask implements ITask {

	private Map<Integer, List<double[]>> paramsMap;

	public HarmonicAnalysisTask() {
		paramsMap = new HashMap<>();
	}

	@Override
	public boolean run() throws IOException, ParseException {
		return true;
	}

	public Date getMidDate(Date d1, Date d2) {
		return new Date((d2.getTime() + d1.getTime()) / 2);
	}

	public void doCalculation(Date startDate, Date endDate, int rawDeltaT, int newDeltaT, int hours,
			List<TideSymbol> tideSymbolList, List<StationDateData> dataList, int state, Path outputPath)
			throws ParseException, IOException {
		 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm_");

		// int divCount = rawDeltaT / newDeltaT;
		// // 进行加密
		// List<StationDateData> denseDataList =
		// BaseHydrologyDataHelper.doProportionalInterpolation(dataList, rawDeltaT,
		// divCount);
		//
		// BaseHydrologyDataHelper.writeToFile(denseDataList,
		// outputPath.resolve("_" + hours + "_" + newDeltaT + "_dense.csv"));

		// 线性插值
		// List<StationDateData> linearDataList =
		// BaseHydrologyDataHelper.doLinearInterpolation(dataList, startDate,
		// endDate, newDeltaT);

		// BaseHydrologyDataHelper.writeToFile(linearDataList,
		// outputPath.resolve("_" + hours + "_" + newDeltaT + "_linear.csv"));
		Date midDate;
		HarmonicHelper harmonicHelper = new HarmonicHelper();
		// 增加时间偏移
		// SimpleDateFormat sdf1 = ConstantHelper.getDateFormat();
		// long nh = 60 * 60 * 1000;
		// double offset_t;
		// Date midDate = getMidDate(sdf1.parse(dataList.get(0).getDate()),
		// sdf1.parse(dataList.get(dataList.size()-1).getDate()));
		// Date tt0 = DateUtil.LDTime2Date(DateUtil.Date2LDTime(sdf1.parse("2017/1/1
		// 0:0")).plusHours(hours / 2));
		// offset_t= (tt0.getTime()-midDate.getTime()) / nh;

		// 求取a ,b 系数
		List<double[]> ab = getAxBx_V1(newDeltaT, hours, tideSymbolList, dataList);
		double[] ax = ab.get(0);
		double[] bx = ab.get(1);
		SimpleDateFormat sdf1 = ConstantHelper.getDateFormat();
		midDate = getMidDate(sdf1.parse(dataList.get(0).getDate()),
				sdf1.parse(dataList.get(dataList.size() - 1).getDate()));
		
		//System.out.printf("%s\t",sdf1.format(midDate));
		
		Date firstDate = sdf1.parse("2017/1/1 0:0:0");

		HarmonicHelper helper = new HarmonicHelper();

		double[] checkRslt = helper.checkRslt_V1(dataList, newDeltaT / 60f, ax, bx);
		
		//helper.saveToFile(dataList, checkRslt, outputPath.resolve("check_Rslt_1.csv"));
		
		double[] rawData = BaseHydrologyDataHelper.hydToDoubleList(dataList);

		
		double k = lineFit(checkRslt, rawData);
		// k=1.0;
		List<double[]> a1b1 = calAx1_Bx1(tideSymbolList, firstDate, midDate, ax, bx,k);


		// for (double val : bx) {
		// System.out.printf("%.4f\t", val);
		// }
		// System.out.println();

		// 粗 验证
		 double[] preDataArr = harmonicHelper.checkRsltAndSave_V1(dataList, newDeltaT
		 / 60f, ax, bx,
		 outputPath.resolve("_check_" + sdf.format(startDate) + hours + "_" +
		 newDeltaT + "_.csv"));

		// 极值比较
		// Path compareRsltPath = outputPath.resolve("highlowCompare.csv");
		// BaseHydrologyDataHelper.higgLowCompare(dataList, preDataArr,compareRsltPath);

		// 求极值之差
		List<StationDateData> extremumList = BaseHydrologyDataHelper.getExtremumTide_V2(dataList);

//		for(StationDateData sData:extremumList) {
//			System.out.printf("%s\t%f\t\n",sData.getDate(),sData.getValue());
//		
//		}
		
		// 求均值
		int exLen = (extremumList.size() / 2) * 2;
		double sum = 0.0, exAvg = 0.0;
		for (int i = 0; i + 1 < exLen; i += 2) {
			sum += Math.abs(extremumList.get(i).getValue() - extremumList.get(i + 1).getValue());
		}
		exAvg = sum / (exLen / 2);
		 if (state == 0) {
		 System.out.printf("%.4f \t\t", exAvg);
		 } else if (state == 1) {
		 // 打印第一个
		 System.out.printf("%.4f \t\t", dataList.get(0).getValue());
		 }


		for (double val : a1b1.get(0)) {
			System.out.printf("%.4f\t", val);
		}
		System.out.println();
		
		// 求解调和常数H
		midDate = DateUtil.LDTime2Date(DateUtil.Date2LDTime(startDate).plusHours(hours / 2));
		double params[][] = harmonicHelper.getInterFactorsAndCorAngle(tideSymbolList, ax, bx, startDate, midDate);
		// System.out.println("H");
//		 System.out.printf("%.4f\t", ax[0]);
//		 for (double val : params[0]) {
//		 System.out.printf("%.4f\t", val);
//		 }
		// for (double val : params[1]) {
		// System.out.printf("%.4f\t", val);
		// }
//	System.out.println();

		// 尝试预测
		// int deltaT = 1; // min
		//
		// Date pStartDate = startDate;
		// Date pEndDate = ConstantHelper.getDateFormat().parse("2005/1/30 0:00:00");
		// PredictionHelper preHelper = new PredictionHelper(pStartDate, tideSymbolList,
		// ax[0], params[0], params[1]);
		// Calendar calendar = DateUtil.Date2Calendar(pStartDate);
		// int count = 0;
		// PathUtil.preparePath(outputPath.resolve("prediction.csv"));
		// try (BufferedWriter bw =
		// Files.newBufferedWriter(outputPath.resolve("prediction.csv"),
		// StandardOpenOption.WRITE)) {
		// bw.write("pre\n");
		// while (calendar.getTime().before(pEndDate)) {
		// bw.write(preHelper.doPrediction(calendar.getTime()) + "\n");
		// calendar.add(Calendar.MINUTE, deltaT);
		// count++;
		// }
		// }
		//
		// System.out.println("total:" + count);

		// harmonicHelper.saveHarmonicParams(tideSymbolList, params,
		// outputPath.resolve("_H_g" + sdf.format(startDate) + "_" + hours + "_" +
		// newDeltaT + ".csv"));
		//
		// // 输出参数
		// harmonicHelper.saveParamsAsFile(ax, bx, outputPath.resolve("_ax_bx_" + hours
		// + "_" + newDeltaT + "_.csv"));

		// 缓存每次参数
		List<double[]> paramsList = new ArrayList<>();
		paramsList.add(ax);
		paramsList.add(bx);
		paramsMap.put(newDeltaT, paramsList);

	}

	public void doCalculation(Date startDate, Date endDate, int rawDeltaT, int newDeltaT, int hours,
			List<TideSymbol> tideSymbolList, List<StationDateData> dataList, Path outputPath)
			throws ParseException, IOException {
		doCalculation(startDate, endDate, rawDeltaT, newDeltaT, hours, tideSymbolList, dataList, 1, outputPath);

	}

	/**
	 * 周期为 [0,T]
	 * 
	 * @param newDeltaT
	 * @param hours
	 * @param tideSymbolList
	 * @param dataList
	 * @return
	 */
	public List<double[]> getAxBx_V2(int newDeltaT, int hours, List<TideSymbol> tideSymbolList,
			List<StationDateData> dataList) {
		HarmonicHelper harmonicHelper = new HarmonicHelper();

		List<double[][]> m = harmonicHelper.getMatrix_V2(dataList, newDeltaT / 60f, tideSymbolList);

		// Matrix m1 = new Matrix(m.get(0));
		// System.out.println(MatrixHelper.equals(m1,m1.transpose()));

		// 寻求合适岭回归系数
		// harmonicHelper.findOffsetVal(m.get(0), m.get(1), 0.01, 20);

		// double[] params1 = harmonicHelper.getMatrixRslt(m.get(0), m.get(1),
		// 0.000).transpose().getArray()[0];

		// MatrixHelper.printMatrix( new
		// Matrix(m.get(1)).transpose().times(1f/hours).getArray(), "\t");

		Matrix ab = harmonicHelper.getMatrixRslt(m.get(0), m.get(1), 0.001, 0.008).transpose();

		// PCA分析
		// PCAHelper pcaHelper = new PCAHelper(m.get(0), m.get(1));
		// Matrix ab = pcaHelper.doPCR_V1(19);

		// PCAHelper pcaHelper = new PCAHelper(m.get(0), m.get(1));
		// pcaHelper.findPCRParam();
		// Matrix ab = pcaHelper.doPCR_V1(19);

		// ab.print(4, 6);

		double[] ax = null, bx = null;
		int tidesCount = tideSymbolList.size();
		ax = ab.getMatrix(0, 0, 0, tidesCount).getArray()[0];
		bx = ab.getMatrix(0, 0, tidesCount + 1, 2 * tidesCount).getArray()[0];
		List<double[]> rslt = new ArrayList<>();
		rslt.add(ax);
		rslt.add(bx);
		return rslt;

	}

	/**
	 * 获取a,b的系数，周期为 [-T/2,T/2]
	 * 
	 * @param newDeltaT
	 * @param hours
	 * @param tideSymbolList
	 * @param dataList
	 * @return
	 * @throws ParseException
	 */
	public List<double[]> getAxBx_V1(int newDeltaT, int hours, List<TideSymbol> tideSymbolList,
			List<StationDateData> dataList) throws ParseException {
		HarmonicHelper harmonicHelper = new HarmonicHelper();

		// List<double[][]> m = harmonicHelper.getMatrix_V1(dataList, newDeltaT / 60f,
		// tideSymbolList);

		List<double[][]> m = harmonicHelper.getMatrix_V1(dataList, newDeltaT / 60f, tideSymbolList);

		 Matrix Ax = new Matrix(m.get(0));
		 Matrix Bx = new Matrix(m.get(2));
//		 MatrixHelper.printMatrix(Ax.times(1/Ax.get(0, 0)).getArray(),"\t");
//		 System.out.println();
//		 MatrixHelper.printMatrix(Bx.times(1/Ax.get(0, 0)).getArray(),"\t");

		double end =0.1;
		double dt = end / 20f;
		double offset = 0.025;

//		 while(offset<=end) {
//		 Matrix ma = harmonicHelper.getMatrixRslt(m.get(2), m.get(3),
//		 offset).transpose();
//		
//		 MatrixHelper.printMatrix(ma.getArray(),"\t");
//		 offset+=dt;
//		 }
		Matrix ma = harmonicHelper.getMatrixRslt(m.get(0), m.get(1), offset).transpose();

		Matrix mb = harmonicHelper.getMatrixRslt(m.get(2), m.get(3), offset).transpose();

		double[] ax = null, bx = null;
		ax = ma.getArray()[0];
		bx = mb.getArray()[0];

		// System.out.println();
		// MatrixHelper.printMatrix(ma.getArray(),"\t");
		// System.out.println();
		// MatrixHelper.printMatrix(mb.getArray(),"\t");

		List<double[]> rslt = new ArrayList<>();
		rslt.add(ax);
		rslt.add(bx);
		return rslt;

	}

	/**
	 * 计算调整后的Ax,Bx
	 * 
	 * @param firstDate
	 * @param midDate
	 * @param ax
	 * @param bx
	 * @return
	 */

	public List<double[]> calAx1_Bx1(List<TideSymbol> tideSymbols, Date firstDate, Date midDate, double[] ax,
			double[] bx,double k) {
		SimpleDateFormat sdf = ConstantHelper.getDateFormat();
		// System.out.println(sdf.format(firstDate));
		// System.out.println(sdf.format(midDate));

		long nh = 60 * 60 * 1000;

		double ti = (midDate.getTime() - firstDate.getTime()) / nh;

		List<double[]> rsltList = new ArrayList<>();

		double[] rsltA = new double[ax.length];
		double[] rsltB = new double[bx.length];
		double w;
		HarmonicHelper harmonicHelper = new HarmonicHelper();
		rsltA[0] = ax[0]*k;
		// ax
		for (int i = 0; i < tideSymbols.size(); i++) {
			w = harmonicHelper.getAngularVelocity(tideSymbols.get(i));
			rsltA[i + 1] = ax[i + 1] * Math.cos(w * ti) - bx[i] * Math.sin(w * ti);
			rsltA[i + 1] *=k;
			rsltB[i] = ax[i + 1] * Math.sin(w * ti) + bx[i] * Math.cos(w * ti);
			rsltB[i]*=k;
		}
		rsltList.add(rsltA);
		rsltList.add(rsltB);
		return rsltList;
	}


	 
	
	 
	/**
	  * 最小二乘法直线拟合（不是常见的一元线性回归算法）
	  * 将离散点拟合为  a x + b y + c = 0 型直线
	  * 假设每个点的 X Y 坐标的误差都是符合 0 均值的正态分布的。
	  * 与一元线性回归算法的区别：一元线性回归算法假定 X 是无误差的，只有 Y 有误差。
	  */
	public double lineFit(double[] pre,double[] raw)
	{
		SimpleRegression regression = new SimpleRegression(false);
		
		for(int i=0;i<pre.length;i++) {
			regression.addData(pre[i], raw[i]);
		}
//		System.out.println(regression.getIntercept());
//		// displays intercept of regression line
//
//		System.out.println(regression.getSlope());
//		// displays slope of regression line
//
//		System.out.println(regression.getSlopeStdErr());
//		// displays slope standard error
		return regression.getSlope();
	}


	
	public void saveParamsInOneFile(Path outputPath) throws IOException {
		PathUtil.preparePath(outputPath);
		StringBuilder sb = new StringBuilder();
		HarmonicHelper hh = new HarmonicHelper();

		try (BufferedWriter bw = Files.newBufferedWriter(outputPath)) {

			paramsMap.entrySet().stream().forEach((Entry<Integer, List<double[]>> entry) -> {
				double[] ax, bx;
				sb.append("DT(min),").append(entry.getKey()).append("\n");
				ax = entry.getValue().get(0);
				bx = entry.getValue().get(1);
				sb.append(hh.paramsAsString(ax, bx)).append("\n\n");
			});
			bw.write(sb.toString());
		}
	}
}
